/*
 * @(#)DocumentEntryTransformer.java
 * Date 2013-04-10
 * Version 1.0
 * DocumentEntryAuthor Jim Horner
 * Copyright (c)2012
 */


package us.hornerscorners.lamppost.registry.transformer.model;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import javax.enterprise.context.ApplicationScoped;

import javax.inject.Inject;

import oasis.names.tc.ebxml_regrep.xsd.rim._3.ClassificationType;
import oasis.names.tc.ebxml_regrep.xsd.rim._3.ExternalIdentifierType;
import oasis.names.tc.ebxml_regrep.xsd.rim._3.ExtrinsicObjectType;
import oasis.names.tc.ebxml_regrep.xsd.rim._3.SlotType1;

import org.apache.commons.lang3.StringUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import us.hornerscorners.lamppost.exception.ConversionException;
import us.hornerscorners.lamppost.exception.ServiceActionError;
import us.hornerscorners.lamppost.exception.ServiceActionException;
import us.hornerscorners.lamppost.exception.SeverityType;
import us.hornerscorners.lamppost.exception.ValidationException;

import us.hornerscorners.lamppost.model.common.TimeInterval;

import us.hornerscorners.lamppost.model.document.DocumentEntry;

import us.hornerscorners.lamppost.registry.constant.AvailabilityStatus;
import us.hornerscorners.lamppost.registry.constant.ErrorCode;
import us.hornerscorners.lamppost.registry.constant.Metadata;
import us.hornerscorners.lamppost.registry.constant.SlotName;
import us.hornerscorners.lamppost.registry.constant.XDSDataType;

import us.hornerscorners.lamppost.registry.model.DataTypeValidator;
import us.hornerscorners.lamppost.registry.model.MetadataValidator;

import us.hornerscorners.lamppost.registry.utils.XDSUtils;

import us.hornerscorners.lamppost.transformer.AbstractTransformer;

import static us.hornerscorners.lamppost.registry.constant.SlotName.Hash;
import static us.hornerscorners.lamppost.registry.constant.SlotName.LanguageCode;
import static us.hornerscorners.lamppost.registry.constant.SlotName.SourcePatientInfo;


/**
 *
 * @author jim
 */
@ApplicationScoped
public class DocumentEntryTransformer
        extends AbstractTransformer<ExtrinsicObjectType, DocumentEntry> {

    /** Field description */
    @Inject
    private AttributeTransformer attributeTransformer;

    /** Field description */
    @Inject
    private AuthorTransformer authorTransformer;

    /** Field description */
    @Inject
    private CodedElementTransformer codedElementTransformer;

    /** Field description */
    @Inject
    private DataTypeValidator dataValidator;

    /** Field description */
    @Inject
    private LocalizedStringTransformer localizedStringTransformer;

    /** Field description */
    private final Logger logger = LoggerFactory.getLogger(getClass());

    /** Field description */
    @Inject
    private MetadataValidator metaValidator;

    /** Field description */
    @Inject
    private PatientIdentifierTransformer patientIdentifierTransformer;

    /** Field description */
    @Inject
    private UniqueIdentifierTransformer uniqueIdentifierTransformer;

    /**
     * Method description
     *
     *
     * @param eo
     *
     * @return
     *
     * @throws ConversionException
     * @throws ValidationException
     */
    @Override
    public DocumentEntry transform(ExtrinsicObjectType eo)
            throws ConversionException, ValidationException {

        DocumentEntry result = new DocumentEntry();

        List<ServiceActionError> errors = new ArrayList<>();

        result.setServiceInterval(new TimeInterval());

        // get attributes
        result.setEntryURN(XDSUtils.assignEntryUUID(eo.getId()));
        result.setEntryVersion(XDSUtils.parseVersionName(eo.getVersionInfo()));
        result.setMimeType(eo.getMimeType());
        result.setAvailabilityStatus(
            this.codedElementTransformer.transform(AvailabilityStatus.Submitted));

        // versioning
        result.setEntryLogicalURN(StringUtils.defaultIfBlank(eo.getLid(),
                result.getEntryURN()));
        result.setEntryVersion(
            StringUtils.defaultIfBlank(
                XDSUtils.parseVersionName(eo.getVersionInfo()), "1"));

        // get slots
        for (SlotType1 slot : eo.getSlot()) {

            boolean validSlot = false;
            String strSlotName = slot.getName();
            SlotName slotName = SlotName.toSlotName(strSlotName);
            String value = XDSUtils.parseFirstValue(slot);

            if (slotName != null) {

                switch (slotName) {

                    case CreationTime :

                        errors.addAll(this.dataValidator.validate(slotName,
                                XDSDataType.dtm, value));
                        result.setCreationTime(XDSUtils.parseDTM(value));
                        validSlot = true;

                        break;

                    case Hash :
                        errors.addAll(this.dataValidator.validate(slotName,
                                XDSDataType.sha1, value));
                        result.setHash(value);
                        validSlot = true;

                        break;

                    case LanguageCode :
                        errors.addAll(this.dataValidator.validate(slotName,
                                XDSDataType.string, value));
                        result.setLanguageCode(value);
                        validSlot = true;

                        break;

                    case RepositoryUniqueId :
                        errors.addAll(this.dataValidator.validate(slotName,
                                XDSDataType.oid, value));
                        result.setRepositoryId(value);
                        validSlot = true;

                        break;

                    case ServiceStartTime :
                        errors.addAll(this.dataValidator.validate(slotName,
                                XDSDataType.dtm, value));

                        result.getServiceInterval().setStart(XDSUtils.parseDTM(value));
                        validSlot = true;

                        break;

                    case ServiceStopTime :
                        errors.addAll(this.dataValidator.validate(slotName,
                                XDSDataType.dtm, value));
                        result.getServiceInterval().setStop(XDSUtils.parseDTM(value));
                        validSlot = true;

                        break;

                    case Size :
                        errors.addAll(this.dataValidator.validate(slotName,
                                XDSDataType.number, value));
                        result.setSize(value);

                        validSlot = true;

                        break;

                    case SourcePatientId :
                        errors.addAll(this.dataValidator.validate(slotName,
                                XDSDataType.cx, value));
                        result.setSourcePatientId(
                            this.patientIdentifierTransformer.transform(slot));
                        validSlot = true;

                        break;

                    case SourcePatientInfo :
                        List<String> values = XDSUtils.parseValues(slot);

                        if (values.isEmpty() == false) {
                            result.getSourcePatientInfo().addAll(values);
                        }

                        validSlot = true;

                        break;

                    case Uri :
                        errors.addAll(this.dataValidator.validate(slotName,
                                XDSDataType.uri, value));
                        result.setUri(XDSUtils.parseFirstValue(slot));
                        validSlot = true;

                        break;
                }
            }

            if (validSlot == false) {

                try {

                    result.getExtraAttributes().add(
                        this.attributeTransformer.transform(slot));

                } catch (ServiceActionException e) {

                    errors.addAll(Arrays.asList(e.getErrors()));
                }
            }

        }

        // names and description
        result.getDisplayNames().addAll(
            this.localizedStringTransformer.transform(eo.getName()));
        result.getDescriptions().addAll(
            this.localizedStringTransformer.transform(eo.getDescription()));

        // **************
        // classification

        for (ClassificationType ctype : eo.getClassification()) {

            Metadata schemeType = Metadata.toMetadata(ctype.getClassificationScheme());

            if (schemeType != null) {

                switch (schemeType) {

                    case DocumentEntryAuthor :
                        result.getAuthors().add(this.authorTransformer.transform(ctype));

                        break;

                    case DocumentEntryClassCode :

                        result.setClassCode(
                            this.codedElementTransformer.transform(ctype));

                        break;

                    case DocumentEntryConfidentialityCode :
                        result.getConfidentialityCodes().add(
                            this.codedElementTransformer.transform(ctype));

                        break;

                    case DocumentEntryEventCode :
                        result.getEventCodes().add(
                            this.codedElementTransformer.transform(ctype));

                        break;

                    case DocumentEntryFormatCode :
                        result.setFormatCode(
                            this.codedElementTransformer.transform(ctype));

                        break;

                    case DocumentEntryHealthCareFacilityTypeCode :
                        result.setHealthcareFacilityTypeCode(
                            this.codedElementTransformer.transform(ctype));

                        break;

                    case DocumentEntryPracticeSettingCode :
                        result.setPracticeSettingCode(
                            this.codedElementTransformer.transform(ctype));

                        break;

                    case DocumentEntryTypeCode :
                        result.setTypeCode(this.codedElementTransformer.transform(ctype));

                        break;
                }
            }
        }

        // *******************
        // externalIdentifiers

        for (ExternalIdentifierType idtype : eo.getExternalIdentifier()) {

            Metadata schemeType = Metadata.toMetadata(idtype.getIdentificationScheme());

            if (schemeType != null) {

                String value = idtype.getValue();

                switch (schemeType) {

                    case DocumentEntryPatientId :

                        errors.addAll(this.dataValidator.validate(schemeType,
                                XDSDataType.cx, value));

                        result.setPatientId(
                            this.patientIdentifierTransformer.transform(idtype));

                        break;

                    case DocumentEntryUniqueId :
                        errors.addAll(this.dataValidator.validate(schemeType,
                                XDSDataType.oid, value));

                        result.setUniqueId(
                            this.uniqueIdentifierTransformer.transform(idtype));

                        break;
                }
            }
        }

        errors.addAll(validate(result));

        if (errors.isEmpty() == false) {

            throw new ValidationException(getErrorLocation(), errors);
        }

        return result;
    }

    /**
     * Method description
     *
     *
     *
     */
    private Collection<ServiceActionError> validate(DocumentEntry entry) {

        List<ServiceActionError> errors = new ArrayList<>();

        errors.addAll(this.metaValidator.validate(Metadata.DocumentEntryClassCode,
                entry.getClassCode(), true));
        errors.addAll(
            this.metaValidator.validate(
                Metadata.DocumentEntryConfidentialityCode,
                entry.getConfidentialityCodes(), false));
        errors.addAll(this.metaValidator.validate(Metadata.DocumentEntryFormatCode,
                entry.getFormatCode(), true));
        errors.addAll(
            this.metaValidator.validate(
                Metadata.DocumentEntryHealthCareFacilityTypeCode,
                entry.getHealthcareFacilityTypeCode(), true));

        errors.addAll(this.metaValidator.validate(SlotName.LanguageCode,
                entry.getLanguageCode()));

        errors.addAll(this.metaValidator.validate(Metadata.DocumentEntryPatientId,
                entry.getPatientId()));

        errors.addAll(
            this.metaValidator.validate(
                Metadata.DocumentEntryPracticeSettingCode,
                entry.getPracticeSettingCode(), true));

        errors.addAll(this.metaValidator.validate(SlotName.RepositoryUniqueId,
                entry.getRepositoryId()));

        if (entry.getServiceInterval() != null) {

            errors.addAll(this.metaValidator.validate(SlotName.ServiceStartTime,
                    SlotName.ServiceStopTime, entry.getServiceInterval()));
        }

        errors.addAll(this.metaValidator.validate(SlotName.Size, entry.getSize()));

        errors.addAll(this.metaValidator.validate(SlotName.SourcePatientId,
                entry.getSourcePatientId()));

        errors.addAll(this.metaValidator.validate(Metadata.DocumentEntryTypeCode,
                entry.getTypeCode(), true));

        errors.addAll(this.metaValidator.validate(Metadata.DocumentEntryUniqueId,
                entry.getUniqueId()));

        if (StringUtils.isBlank(entry.getMimeType())) {

            errors.add(
                new ServiceActionError(
                    ErrorCode.XDSRegistryMetadataError.getCode(),
                    "mimeType is required.", SeverityType.Error));
        }

        return errors;
    }
}
